package org.ihelpuoo.localize;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;

/**
 * <b>SimpleDate class:</b>
 * <hr color=grey size=1>
 *
 *
 * @see  http://blog.csdn.net/neweroica/articles/315397.aspx
 * @author zozoh  created @ 2007
 *
 */
public class Date implements Comparable<Date>
{
	private static String	DEFAULT_PATTERN	= "yyyy-MM-dd";

	public static Date createInstance(int year, int month, int day)
	{
		return new Date(year,month,day);
	}

	public static Date createInstance(String s) throws ParseException
	{
		Date d = new Date();
		d.valueOf(s);
		return d;
	}

	public static Date createInstance(String s, String pattern) throws ParseException
	{
		Date d = new Date();
		d.valueOf(s,pattern);
		return d;
	}

	public static Date createInstance(long ms)
	{
		Date d = new Date();
		d.valueOf(ms);
		return d;
	}

	/**
	 * 1 base
	 */
	private int	year;
	/**
	 * 1 base
	 */
	private int	month;
	/**
	 * 1 base
	 */
	private int	day;

	protected Date()
	{
		year = 1970;
		month = 1;
		day = 1;
	}

	protected Date(int year, int month, int day)
	{
		this.setYear(year);
		this.setMonth(month);
		this.setDay(day);
	}

	public Date offsetDay(int off)
	{
		if (off == 0)
			return this;
		if (off > 0)
			for (int i = 0; i < off; i++)
				addOneDay();
		else
			for (int i = off; i < 0; i++)
				minusOneDay();
		return this;
	}

	public Date offsetDayOnly(int off)
	{
		if (off == 0)
			return this;
		day += off;
		if (day < 1)
			day = 1;
		else if (day > getMaxDayOfMonth())
			day = getMaxDayOfMonth();
		return this;
	}

	public Date offsetMonth(int off)
	{
		if (off == 0)
			return this;
		if (off > 0)
			for (int i = 0; i < off; i++)
				addOneMonth();
		else
			for (int i = off; i < 0; i++)
				minusOneMonth();
		return this;
	}

	public Date offsetMonthOnly(int off)
	{
		if (off == 0)
			return this;
		month += off;
		if (month < 1)
			month = 1;
		else if (month > 12)
			month = 12;
		return this;
	}

	public Date offsetYear(int off)
	{
		if (off == 0)
			return this;
		year += off;
		if (year < 1)
			year = 1;
		else if (year > 9999)
			year = 9999;
		return this;
	}

	public Date minusOneDay()
	{
		if (day > 1)
			day--;
		else
		{
			day = minusOneMonth().getMaxDayOfMonth(month);
		}
		return this;
	}

	public Date minusOneMonth()
	{
		if (month > 1)
			month--;
		else
		{
			minusOneYear();
			month = 12;
		}
		return this;
	}

	public Date minusOneYear()
	{
		if (year > 0)
			year--;
		return this;
	}

	public Date addOneDay()
	{
		if (day < getMaxDayOfMonth(month))
			day++;
		else
		{
			addOneMonth();
			day = 1;
		}
		return this;
	}

	public Date addOneMonth()
	{
		if (month < 12)
			month++;
		else
		{
			year++;
			month = 1;
		}
		return this;
	}

	public Date addOneYear()
	{
		if (year < 9999)
			year++;
		return this;
	}

	private int	_ily	= -1;	/* -1, without init, 0 leap year, 1 normal year*/

	public boolean isLeapYear()
	{
		if (-1 == _ily)
		{
			if (year % 400 == 0)
				_ily = 0;
			else if (year % 100 == 0)
				_ily = 1;
			else if (year % 4 == 0)
				_ily = 0;
			else
				_ily = 1;
		}
		return _ily == 0;
	}

	/**
	 * @return total of previous leap year, current year exclusive.
	 */
	public int countPreviousLeapYear()
	{
		int total = year - 1;
		return total / 4 - total / 100 + total / 400;
	}

	private static int[]	MONTH_SUM_DAY		= {0,31,59,90,120,151,181,212,243,273,304,334,365};
	private static int[]	MAX_DAY_IN_MONTH	= {0,31,28,31,30,31,30,31,31,30,31,30,31};
	private static int[]	LMONTH_SUM_DAY		= {0,31,60,91,121,152,182,213,244,274,305,335,366};
	private static int[]	LMAX_DAY_IN_MONTH	= {0,31,29,31,30,31,30,31,31,30,31,30,31};

	public int getDayInYear()
	{
		return this.getSumDayOfMonth(month - 1) + day;
	}

	public int getSumDayOfMonth(int month)
	{
		if (month > 12 || month < 0)
			return -1;
		if (this.isLeapYear())
			return LMONTH_SUM_DAY[month];
		return MONTH_SUM_DAY[month];
	}

	public int getMaxDayOfMonth(int month)
	{
		if (month > 12 || month < 0)
			return -1;
		if (this.isLeapYear())
			return LMAX_DAY_IN_MONTH[month];
		return MAX_DAY_IN_MONTH[month];
	}

	public int getMaxDayOfMonth()
	{
		return this.getMaxDayOfMonth(this.month);
	}

	/**
	 * @return 0~6, 0 is mean sunday
	 */
	public int getWeekDay()
	{
		return this.getDayInAD() % 7;
	}

	public int getMonthFirstWeekDay()
	{
		int wd = this.getWeekDay();
		if (this.day == 1)
			return wd;
		wd = wd - this.day % 7 + 1;
		if (wd < 0)
			return 7 + wd;
		return wd;
	}

	public int getDayInAD()
	{
		return (year - 1) * 365 + countPreviousLeapYear() + getDayInYear();
	}

	public int getDay()
	{
		return day;
	}

	public void setDay(int day)
	{
		if (day < 1)
			this.day = 1;
		else
		{
			int max = this.getMaxDayOfMonth(this.month);
			if (day > max)
				this.day = max;
			else
				this.day = day;
		}
	}

	public int getMonth()
	{
		return month;
	}

	public void setMonth(int month)
	{
		if (month < 1)
			this.month = 1;
		else if (month > 12)
			this.month = 12;
		else
			this.month = month;
		if (this.day > this.getMaxDayOfMonth(this.month))
			this.day = this.getMaxDayOfMonth(this.month);
	}

	public int getYear()
	{
		return year;
	}

	public void setYear(int year)
	{
		_ily = -1;
		if (year < 0)
			this.year = 0;
		else
			this.year = year;
		if (this.day > this.getMaxDayOfMonth(this.month))
			this.day = this.getMaxDayOfMonth(this.month);
	}

	public void valueOf(String s) throws ParseException
	{
		this.valueOf(s,DEFAULT_PATTERN);
	}

	public void valueOf(String s, String pattern) throws ParseException
	{
		SimpleDateFormat format = new SimpleDateFormat(pattern);
		java.util.Date date = format.parse(s);
		this.valueOf(date.getTime());
	}

	public void valueOf(long ms)
	{
		Calendar c = Calendar.getInstance();
		c.setTimeInMillis(ms);
		year = c.get(Calendar.YEAR);
		month = c.get(Calendar.MONTH) + 1;
		day = c.get(Calendar.DAY_OF_MONTH);
	}

	@Override
	public String toString()
	{
		StringBuffer sb = new StringBuffer();
		sb.append(year);
		sb.append('-');
		sb.append(String.format("%02d",month));
		sb.append('-');
		sb.append(String.format("%02d",day));
		return sb.toString();
	}

	@Override
	public Object clone() throws CloneNotSupportedException
	{
		return new Date(year,month,day);
	}

	@Override
	public boolean equals(Object obj)
	{
		if (((Date)obj).year != year)
			return false;
		if (((Date)obj).month != month)
			return false;
		if (((Date)obj).day != day)
			return false;
		return true;
	}

	@Override
	public int hashCode()
	{
		return year + month + day;
	}

	public int compareTo(Date date)
	{
		if (this.equals(date))
			return 0;
		if (year > date.year)
			return 1;
		if (month > date.month)
			return 1;
		if (day > date.day)
			return 1;
		return -1;
	}

}
